package com.easydl.project.service;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URISyntaxException;
import java.util.Properties;


import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache.ValueWrapper;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import com.easydl.project.model.EasydlAccessTokenVO;

//@Service
public class EasyTokenService {
	
	Logger logger=LoggerFactory.getLogger(this.getClass());
	
	@Qualifier(value="easyRestTemplate")
	@Autowired
	private RestTemplate easyRestTemplate;
	
	@Qualifier(value="easyConcurrentMapCache")
	@Autowired
	private ConcurrentMapCache easyConcurrentMapCache;
	
	@Value("${easydl.clientId}")
	private String clientId;
	
	@Value("${easydl.clientSecret}")
	private String clientSecret;
	
	@Value("${easydl.accessToken.url}")
	private String accessTokenUrl;
	
	@Value("${easydl.accessToken.fileName}")
	private String fileName;
	
	/**
	 * 本地缓存，本地，远程
	 * @return
	 */
	public EasydlAccessTokenVO getToken() {
		//从本地缓存获取
		EasydlAccessTokenVO easydlAccessTokenVO=this.getCacheToken();
		
		long createDateTime=easydlAccessTokenVO.getDateTime();
		long nowDateTime=System.currentTimeMillis();
		//过期了
		if((createDateTime+easydlAccessTokenVO.getExpires_in())>nowDateTime) {
			logger.info("本地缓存过期，直接去远程拿并放入本地及本地缓存");
			//远程获取，放入本地
			easydlAccessTokenVO=this.getRemoteToken();
			this.writeLocalToken(easydlAccessTokenVO);
		}
		//从本地文件读取
		return easydlAccessTokenVO;
	}
	
	/**
	 * 刷洗缓存
	 * @return
	 */
	public EasydlAccessTokenVO reflashToken() {
		//远程获取，放入本地
		EasydlAccessTokenVO easydlAccessTokenVO=this.getRemoteToken();
		this.writeLocalToken(easydlAccessTokenVO);
		return easydlAccessTokenVO;
	}
	
	
	/**
	 * 1，先从本地缓存拿，本地缓存拿到没过期直接返回
	 * 		本地缓存没拿到去本地拿，本地没过期直接返回，本地过期去远程拿
	 * 2，本地缓存过期去远程拿，拿到放入本地，最后放入本地缓存
	 * @return
	 */
	private EasydlAccessTokenVO getCacheToken() {
		ValueWrapper valueWrapper=easyConcurrentMapCache.get("easydl::access_token");
		if(valueWrapper==null) {
			logger.info("本地缓存没拿到token，去本地文件拿");
			//从本地文件获取
			EasydlAccessTokenVO easydlAccessTokenVO=this.readLocalToken();
			if(easydlAccessTokenVO.getExpires_in()<0) {
				logger.info("本地没有，去远程拿token");
				//远程获取，放入本地
				easydlAccessTokenVO=this.getRemoteToken();
				this.writeLocalToken(easydlAccessTokenVO);
			}else {
				long createDateTime=easydlAccessTokenVO.getDateTime();
				long nowDateTime=System.currentTimeMillis();
				//过期了
				if((createDateTime+easydlAccessTokenVO.getExpires_in())>nowDateTime) {
					logger.info("本地过期，去远程拿token");
					//远程获取，放入本地
					easydlAccessTokenVO=this.getRemoteToken();
					this.writeLocalToken(easydlAccessTokenVO);
				}else {
					//缓存
					this.easyConcurrentMapCache.put("easydl::access_token", easydlAccessTokenVO);
				}
			}
			//从本地文件读取
			return easydlAccessTokenVO;
		}else {
			EasydlAccessTokenVO easydlAccessTokenVO=(EasydlAccessTokenVO)valueWrapper.get();
			return easydlAccessTokenVO;
		}
		
	}
	
	/**
	 * 从远程拿，并放入本地缓存
	 * @return
	 */
	private EasydlAccessTokenVO getRemoteToken() {
        String getAccessTokenUrl = this.accessTokenUrl+"?"
                // 1. grant_type为固定参数
                + "grant_type=client_credentials"
                // 2. 官网获取的 API Key
                + "&client_id=" + this.clientId
                // 3. 官网获取的 Secret Key
                + "&client_secret=" + this.clientSecret;
        logger.info("远程获取token"+getAccessTokenUrl);
        EasydlAccessTokenVO easydlAccessTokenVO=easyRestTemplate.getForObject(getAccessTokenUrl, EasydlAccessTokenVO.class);
        easydlAccessTokenVO.setDateTime(System.currentTimeMillis()+300000);
        this.easyConcurrentMapCache.put("easydl::access_token", easydlAccessTokenVO);
        return easydlAccessTokenVO;
	}
	
	/**
	 * 写入本地
	 * @param easydlAccessTokenVO
	 */
	private void writeLocalToken(EasydlAccessTokenVO easydlAccessTokenVO) {
		logger.info("写入本地文件："+easydlAccessTokenVO.toString());
	    Properties props = new Properties();
	    BufferedWriter bw = null;
	    try {
	    	String filePath = Profile.class.getClassLoader().getResource(fileName).toURI().getPath();
	 	    logger.info("propertiesPath:" + filePath);
	        // 写入属性文件
	        bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath)));
	        // 清空旧的文件
	        //props.clear();
	        props.setProperty("easydl.token.expires_in", String.valueOf(easydlAccessTokenVO.getExpires_in()));
	        props.setProperty("easydl.token.access_token", String.valueOf(easydlAccessTokenVO.getAccess_token()));
	        props.setProperty("easydl.token.dataTime", String.valueOf(easydlAccessTokenVO.getDateTime()));
	        props.store(bw, "改变数据");
	        bw.close();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (URISyntaxException e) {
			e.printStackTrace();
		} finally {
	        try {
	            bw.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
		
	}
	
	/**
	 * 读取本地
	 * @return
	 */
	private EasydlAccessTokenVO readLocalToken() {
		EasydlAccessTokenVO easydlAccessTokenVO=new EasydlAccessTokenVO();
	    Properties props = new Properties();
	    BufferedReader br = null;
	    try {
	    	   //获取文件路径
		    String filePath = Profile.class.getClassLoader().getResource(fileName).toURI().getPath();
		    logger.info("propertiesPath:" + filePath);
	        // 从输入流中读取属性列表（键和元素对）
	        br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
	        props.load(br);
	        easydlAccessTokenVO.setAccess_token(props.getProperty("easydl.token.access_token",""));
	        String expires_in=props.getProperty("easydl.token.expires_in");
	        if(StringUtils.isEmpty(expires_in)) {
	        	easydlAccessTokenVO.setExpires_in(-1L);
	        }else{
	        	easydlAccessTokenVO.setExpires_in(Long.valueOf(expires_in));
	        }
	        String dataTime=props.getProperty("easydl.token.dataTime");
	        if(StringUtils.isEmpty(dataTime)) {
	        	easydlAccessTokenVO.setDateTime(0L);
	        }else{
	        	easydlAccessTokenVO.setDateTime(Long.valueOf(dataTime));
	        }
	        br.close();
	    } catch (IOException e) {
	        e.printStackTrace();
	    } catch (URISyntaxException e) {
			e.printStackTrace();
		} finally {
	        try {
	            br.close();
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
	    logger.info("读取本地文件："+easydlAccessTokenVO.toString());
		return easydlAccessTokenVO;
	}
}
